package cn.tramy.pinxian.common.util


import cn.tramy.pinxian.common.exception.BaseException
import cn.tramy.pinxian.common.exception.CommonErrorCodes
import cn.tramy.pinxian.common.exception.CommonException
import cn.tramy.pinxian.common.util.func.ConditionalExceptionFunc
import cn.tramy.pinxian.common.util.func.LoadCheckExceptionFunc
import cn.tramy.pinxian.common.util.func.LoadConditionThenFunc
import com.github.pagehelper.PageHelper
import com.github.pagehelper.PageInfo
import org.slf4j.LoggerFactory
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import java.util.*
import java.util.concurrent.Executors
import java.util.function.Consumer
import java.util.function.Supplier

//import com.netflix.spectator.api.Registry;
//import com.netflix.spectator.api.Timer;

/**
 * Created by yanjunhua on 16/5/30.
 */
object ExecuteTool {
    private val LOGGER = LoggerFactory.getLogger(ExecuteTool::class.java)
    private val EXECUTOR_SERVICE = Executors.newFixedThreadPool(20)
    private val KEY_COMMON_PAGE_INFO = "commonPageInfo"

    /**
     * 用try获取RuntimeException,并封装成Result返回
     *
     * @param runnable
     * @return
     */
    fun executeWithTry(runnable: () -> Unit): Result<Any> {
        var result: Result<Any>
        try {
            runnable()
            result = Result.OK()
        } catch (ex: BaseException) {
            LOGGER.info("Got  BaseException:[{}]", ex)
            result = Result.FAIL(ex.code, ex.message ?: "Unknown", ex.data)
        } catch (ex: CommonException) {
            LOGGER.debug("Got CommonException [$ex]")
            result = Result.FAIL(CommonErrorCodes.COMMON_COMMON_EXCEPTION, ex.message ?: "Unkown")
        } catch (ex: RuntimeException) {
            LOGGER.info("Got  RuntimeException:[{}]", ex)
            result = checkRuntimeException(ex)
        }

        return result
    }

    /**
     * 异步执行查询并用try封装，查询结果通过callback返回
     * 注意：无法获取线程变量，需要线程变量请传入
     *
     * @param supplierFunc
     * @param callback
     * @param <R>
    </R> */

    fun <R> asyncExecuteQueryWithTry(supplierFunc: Supplier<R>, callback: Consumer<R>) {
        Objects.requireNonNull(callback, "Call back can not be Null")
        EXECUTOR_SERVICE.submit {
            val result = supplierFunc.get()
            callback.accept(result)
        }
    }

    /**
     * 异步执行功能，并在成功后执行callback
     * 注意：无法获取线程变量，需要线程变量请传入
     *
     * @param func
     * @param callback
     */
    @JvmOverloads
    fun asyncExecuteThen(func: Runnable, callback: Runnable? = null) {
        EXECUTOR_SERVICE.submit {
            func.run()
            Optional.ofNullable(callback).ifPresent { runnable -> runnable.run() }
        }
    }


    private fun checkRuntimeException(ex: RuntimeException): Result<Any> {
        var result: Result<*>

        val throwable = this.getRealCause(ex)
        val errorMsg = throwable.message ?: "Unknown"

        result = if (throwable is BaseException) {
            Result.FAIL(throwable.code, errorMsg)
        } else {
            val errorCode = if (throwable is CommonException) CommonErrorCodes.COMMON_COMMON_EXCEPTION else CommonErrorCodes.COMMON_UNKNOWN_EXCEPTION
            LOGGER.info("Got  Exception:[{}]", errorMsg)
            Result.FAIL(errorCode, errorMsg)
        }
        return result
    }

    private fun getRealCause(ex: Throwable): Throwable {

        var result: Throwable = ex

        val nextCause = result.cause
        if (nextCause != null) {
            result = getRealCause(nextCause)
        }

        return result
    }

    //    public static <R> R executeQueryWithTimer(Supplier<R> func, Registry registry, String name, String... tags)
    //    {
    //
    //        R result = null;
    //        ResultContainer<R> resultContainer = new ResultContainer<R>();
    //        Timer timer = registry.timer(name, tags);
    //        timer.record(() ->
    //        {
    //            R funcResult = func.get();
    //            resultContainer.setResult(funcResult);
    //        });
    //        return resultContainer.getResult();
    //    }

    /**
     * 同步执行查询功能，返回结果封装到Result
     *
     * @param supplierFunc
     * @return
     */
    fun executeQueryWithTry(block: () -> Any?): Result<Any> {
        var result: Result<Any>
        try {
            val data = block()
            result = Result.OK(data ?: "")

            val pageInfo = ExecuteContext.current().getValue(KEY_COMMON_PAGE_INFO)

//            val servletRequestAttributes = RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes
//            val pageInfo = servletRequestAttributes.getAttribute(KEY_COMMON_PAGE_INFO, RequestAttributes.SCOPE_REQUEST)

            if (Objects.nonNull(pageInfo)) {
                result.pageInfo = pageInfo as CommonPageInfo
            }
        } catch (ex: BaseException) {
            LOGGER.info("Got  Exception:[{}]", ex.code)
            result = Result.FAIL(ex.code, "")
        } catch (ex: RuntimeException) {
            result = checkRuntimeException(ex)
        }

        return result
    }

    /**
     * conditionFunc返回true时抛出异常
     *
     * @param conditionFunc 检查的条件函数
     * @param exceptionFunc 提供抛出的异常
     */

    fun conditionalException(conditionFunc: () -> Boolean, exceptionFunc: () -> BaseException) {
        //        if (conditionFunc.getAsBoolean())
        //        {
        //            BaseException newException = exceptionFunc.get();
        //            LOGGER.info("Condition is True, throw exception [{}]", newException.getCode());
        //            throw newException;
        //        }
        ifTrue(conditionFunc).exception(exceptionFunc).execute()
    }


    /**
     * conditionFunc返回true时抛出invalidOperation异常
     *
     * @param conditionFunc
     */
    fun invalidOperationIf(conditionFunc: () -> Boolean) {
        ifTrue(conditionFunc).exception { CommonException.invalidOperation() }.execute()
    }


    /**
     * 加载数据后进行异常判断和后续处理
     *
     * @param loadFunc
     * @param <T>
     * @return
    </T> */
    fun <T> loadAndThen(loadFunc: () -> T?): LoadConditionThenFunc<T> {
        return LoadConditionThenFunc(loadFunc)
    }

    /**
     * 新建数据后进行异常判断和后续处理
     *
     * @param loadFunc
     * @param <T>
     * @return
     * @see .loadAndThen
    </T> */
    fun <T> newInstance(loadFunc: () -> T?): LoadConditionThenFunc<T> {
        return LoadConditionThenFunc(loadFunc)
    }


    /**
     * 满足条件抛出异常
     *
     * @param conditionFunc
     * @return
     */
    fun ifTrue(conditionFunc: () -> Boolean): ConditionalExceptionFunc {
        return ConditionalExceptionFunc(conditionFunc)
    }


    /**
     * 加载数据后进行异常判断
     *
     * @param loadFunc
     * @param <T>
     * @return
    </T> */
    fun <T> loadAndCheck(loadFunc: () -> T?): LoadCheckExceptionFunc<T> {
        return LoadCheckExceptionFunc<T>(loadFunc)
    }

    /**
     * 参数为空抛出参数异常
     */
    fun checkParameterNonNull(para: Any) {
        conditionalException({ Objects.isNull(para) }, { CommonException.parameterError() })
    }

    fun checkParameterNonNull(para: Any, parameterName: String) {
        conditionalException({ Objects.isNull(para) }, { CommonException.parameterRequired(parameterName) })
    }

    /**
     * 检查字符串是否为Null或者空串，如果是就抛出异常
     *
     * @param para
     */
    fun checkStringParameterNonNullOrEmpty(para: String) {
        conditionalException({ Objects.isNull(para) || para.isEmpty() }, { CommonException.parameterError() })
    }


    fun checkStringParameterNonNullOrEmpty(para: String, parameterName: String) {
        conditionalException({ Objects.isNull(para) || para.isEmpty() }, { CommonException.parameterRequired(parameterName) })
    }


    fun <T : List<Any>> queryPageData(queryFunc: () -> T): T {
        LOGGER.debug("query page data.")
        val servletRequestAttributes = RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes
        val httpServletRequest = servletRequestAttributes.request
        var queryUrl = httpServletRequest.getRequestURL().toString()


        var strPageNo: String? = null
        var strPageSize: String? = null
        val stringStringMap = httpServletRequest.parameterMap
        if (!stringStringMap.isEmpty()) {
            val paras = ArrayList<String>()
            stringStringMap.keys.forEach { s ->
                if (CommonPageInfo.PARAMETER_PAGE_NO != s && CommonPageInfo.PARAMETER_PAGE_SIZE != s) {
                    val vs = stringStringMap[s]

                    vs?.let {
                        for (v in it) {
                            paras.add("$s=$v")
                        }
                    }

                }
            }


            stringStringMap[CommonPageInfo.PARAMETER_PAGE_NO]?.let {
                if (it.isNotEmpty()) {
                    strPageNo = it[0]
                    LOGGER.debug("Found PageNo:[{}]", strPageNo)
                }
            }



            stringStringMap.get(CommonPageInfo.PARAMETER_PAGE_SIZE)?.let {
                if (it.isNotEmpty()) {
                    strPageSize = it[0]
                    LOGGER.debug("Found PageSize:[{}]", strPageSize)
                }
            }


            val paraStr = paras.joinToString("&")
            queryUrl = "$queryUrl?$paraStr"
        }


        var pageNo = 1
        var pageSize = 10

        if (Objects.nonNull(strPageNo)) {
            try {
                pageNo = Integer.valueOf(strPageNo!!)
            } catch (ex: NumberFormatException) {
                LOGGER.warn("Parameter [pageNo] is not Number,skip")
            }

        }

        if (Objects.nonNull(strPageSize)) {
            try {
                pageSize = Integer.valueOf(strPageSize!!)
            } catch (ex: NumberFormatException) {
                LOGGER.warn("Parameter [pageSize] is not Number,skip")
            }

        }


        val pageRequest = PageRequest.validateAndBuild(pageNo, pageSize)
        PageHelper.startPage<Any>(pageRequest.pageNo, pageRequest.pageSize)
        val result = queryFunc()
        val pageInfo = PageInfo(result)

        val commonPageInfo = CommonPageInfo(queryUrl, pageInfo)

//        servletRequestAttributes.setAttribute(KEY_COMMON_PAGE_INFO, commonPageInfo, RequestAttributes.SCOPE_REQUEST)

        ExecuteContext.current().putValue(KEY_COMMON_PAGE_INFO, commonPageInfo)

        return result
    }
}
/**
 * 异步执行
 * 注意：无法获取线程变量，需要线程变量请传入
 *
 * @param func
 */
